Skip to content

Fix missing SVGs from barrel re-exports and unbuilt local SVG map#1329

Draft
jonathanmos wants to merge 10 commits into
developfrom
jmoskovich/fix-svg-missing-in-replay
Draft

Fix missing SVGs from barrel re-exports and unbuilt local SVG map#1329
jonathanmos wants to merge 10 commits into
developfrom
jmoskovich/fix-svg-missing-in-replay

Conversation

@jonathanmos

@jonathanmos jonathanmos commented Jul 7, 2026

Copy link
Copy Markdown
Member

What does this PR do?

SVGs (icons, logos) were silently missing from Session Replay recordings. Root cause: buildSvgMap() never actually ran due to an initialization-order bug, and even when scanning worked, barrel re-exports and cross-file name collisions were resolved incorrectly. This PR fixes the pipeline end-to-end and adds regression coverage for each issue.

Before
Screenshot 2026-07-26 at 15 09 36

After
Screenshot 2026-07-26 at 15 09 56

Changes
Core fixes

  • Call setApiTypes before buildSvgMap in both the plugin's pre() hook and the generate-sr-assets CLI — buildSvgMap() silently no-op'd without this, meaning the SVG scan never ran in any real build.

  • Reuse the ReactNativeSVG instance across files in pre() instead of rebuilding (and rescanning) it per file.

  • Fix barrel re-export naming: export { default as Logo } from './icon.svg' was keyed under 'default' instead of 'Logo', so aliased re-exports were never matched.

  • Scope local SVG resolution to the importing file's actual bindings — the main fix. Previously a JSX tag was matched by name alone against a project-wide map, so an unrelated component sharing a name with some SVG elsewhere in the project could get wrongly wrapped (or vice versa). Resolution now traces each usage to its real import binding before treating it as an SVG.

  • Always attempt SVG processing regardless of missing filename (a stricter guard introduced during this work was skipping the tag path unnecessarily).

  • Fix svg-map.json package-root resolution so it works when running from source (tests, dev) and not just the built package.

Other

  • Render a placeholder wireframe (instead of nothing) for SVG views with missing entry data, so masked/unavailable SVGs at least show their bounding box.

Tests

  • Add regression tests exercising the real pre()/CLI construction paths (previously every test injected a pre-built instance, so the ordering bug itself was untested).
    Harden tests against a real local svg-map.json cache file interfering with results.

Motivation

What inspired you to submit this pull request?

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
  • If this PR is auto-generated, please make sure also to manually update the code related to the change

@jonathanmos

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dce270e341

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/react-native-babel-plugin/src/index.ts
@jonathanmos
jonathanmos force-pushed the jmoskovich/fix-svg-missing-in-replay branch from 499852d to e82aa14 Compare July 26, 2026 10:14
@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Jul 26, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: ac44218 | Docs | Datadog PR Page | Give us feedback!

SvgViewMapper returned an empty wireframe list when an SVG's hash or
resource entry data was unavailable, making the element fully invisible
in the replay instead of showing at least its bounding box.
buildSvgMap() is a no-op until setApiTypes() has been called (it
guards on this.t). Both the plugin's pre() hook and the
generate-sr-assets CLI called buildSvgMap() first, so the project-wide
SVG scan silently produced an empty map on every real build.
For `export { default as Logo } from './icon.svg'`, the scan keyed
the map entry on spec.local.name ('default') instead of spec.exported
('Logo') -- the name consumers actually import and render as <Logo/>.
Aliased re-exports were therefore never matched.
pre() assigned options.__internal_reactNativeSVG directly with no
fallback, so in a real build (no injected instance) every file
discarded the previous scan and rebuilt/rescanned from scratch.
HandlerResolver/LocalSvgHandler matched purely on JSX tag name against
a project-wide map, with no check that the current file actually
imported that name from an SVG. A plain component <Icon/> in one file
could get wrapped as an unrelated SVG also named Icon imported or
re-exported elsewhere in the project.

resolveSvgImport now resolves each usage site through the JSX tag's
real scope binding, tracing it to its source module (direct .svg
import or barrel re-export via svgFileMap) before treating it as SVG.
Gating processItem() on state.filename being truthy skipped all SVG
handling -- including the <Svg> tag path, which never needed a
filename -- whenever filename was falsy. resolveSvgImport already
fails closed on its own when given an empty currentFile.
The package-root computation was a hardcoded ../../../.. offset from
__dirname, correct only when running from the built lib/commonjs/...
output. Under ts-jest or ts-node it resolved one directory too high,
into the shared monorepo directory instead of this package's own root.
resolvePackageRoot now walks up to find this package's actual
package.json.
@jonathanmos
jonathanmos force-pushed the jmoskovich/fix-svg-missing-in-replay branch from e82aa14 to c80d43c Compare July 26, 2026 11:09
Every existing test injected __internal_reactNativeSVG pre-built with
setApiTypes() already called, bypassing pre()'s own instance
construction entirely -- meaning the setApiTypes/buildSvgMap ordering
fix could regress without any test catching it. Adds a test that lets
pre() build its own instance, and a test that runs the actual
generateSessionReplayAssets() CLI entrypoint end-to-end.
A stray svg-map.json at this package's real root (e.g. left behind by
a local datadog-generate-sr-assets run) would make every test in this
file silently read stale cached data instead of scanning its own tmp
fixtures. Hide that one path from fs.existsSync unconditionally.
@jonathanmos
jonathanmos force-pushed the jmoskovich/fix-svg-missing-in-replay branch from c80d43c to ae922b2 Compare July 26, 2026 12:20
References like "Fix 2, 3, 4, 6" only made sense during the session
that introduced them and mean nothing to a future reader. Replaced
with descriptions of what each case actually verifies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants